package xin.alum.aim.handler;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.Attribute;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import xin.alum.aim.AIM;
import xin.alum.aim.config.AimProperties;
import xin.alum.aim.constant.ChannelAttr;
import xin.alum.aim.constant.ChannelClose;
import xin.alum.aim.util.ApplicationContextUtil;

/**
 * @auther Alum(alum @ live.cn)
 * @date 2021/8/26 15:03
 */
public abstract class BaseServerHandler<T> extends SimpleChannelInboundHandler<T> {

    protected final InternalLogger logger = InternalLoggerFactory.getInstance(this.getClass());

    /**
     * 已知连接关闭原因
     */
    private ChannelClose channelClose = ChannelClose.CLIENT_CLOSE;

    /**
     * 获取 AIM 配置信息
     *
     * @return
     */
    protected AimProperties getProperties() {
        return ApplicationContextUtil.getBean(AimProperties.class);
    }

    /**
     * 获取接受对象
     *
     * @return
     */
    protected AimReceive getAimReceiver() {
        return ApplicationContextUtil.getBean(AimReceive.class);
    }

    /**
     * 关闭连接
     *
     * @param ch
     * @param channelClose
     */
    public void onClose(Channel ch, ChannelClose channelClose) {
        this.channelClose = channelClose;
        if (ch.isOpen()) {
            ch.closeFuture();
            ch.close();
        }
    }

    /**
     * 添加
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        super.handlerAdded(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        logger.error("{}Exception", ctx.channel(), cause);
        this.channelClose = ChannelClose.EXCEPTION;
    }

    /**
     * 注册
     *
     * @param ctx
     * @throws Exception
     */

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
    }

    /**
     * 活跃
     *
     * @param ctx
     * @throws Exception
     */

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
    }


    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        AIM.request.onClose(ctx.channel(), channelClose == null ? ChannelClose.CLOSE : channelClose);
        super.channelInactive(ctx);
    }

    @Override
    protected abstract void channelRead0(ChannelHandlerContext channelHandlerContext, T t) throws Exception;

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        super.channelReadComplete(ctx);
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        super.channelUnregistered(ctx);
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        super.handlerRemoved(ctx);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
        if (!(evt instanceof IdleStateEvent)) {
            return;
        }

        if (((IdleStateEvent) evt).state() == IdleState.ALL_IDLE) {
            heartBeat(ctx.channel());
        }
    }

    /**
     * 心跳超时逻辑处理
     *
     * @param ch
     */
    protected void heartBeat(Channel ch) {
        Attribute<Integer> pin = ch.attr(ChannelAttr.PING_COUNT);
        int pingCount = (pin.get() == null ? 0 : pin.get());
        if (pingCount > getProperties().getIdle().getTyrCount()) {
            this.onClose(ch, ChannelClose.HEARTBEAT);
        } else {
            ch.attr(ChannelAttr.PING_COUNT).set(++pingCount);
        }
    }
}
